package com.free.bsf.apollo;

import com.free.bsf.core.util.*;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;
import lombok.val;
import lombok.var;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;


public class ApolloExtend {


    private static volatile boolean isLoadHookPostProcessBeanFactory=false;
    public static void hookPostProcessBeanFactory(){
        //com.ctrip.framework.apollo.spring.config.PropertySourcesProcessor
        if(!PropertyUtils.getPropertyCache(ApolloProperties.BsfApolloLocalFirstEnabled,true))
            return;
        try {
            String code = "{"+
                    "		$0.postProcessBeanFactoryOld($$);\n" +
                    "       com.free.bsf.apollo.ApolloExtend.refreshApolloPropertySources();\n"+
                    "}";
            //EurekaClientAutoConfiguration.setupJmxPort
            ClassPool classPool = ClassPoolUtils.getInstance();
            CtClass ctClass = classPool.get("com.ctrip.framework.apollo.spring.config.PropertySourcesProcessor");
            if (!isLoadHookPostProcessBeanFactory) {
                isLoadHookPostProcessBeanFactory = true;
                CtMethod ctMethod = ctClass.getDeclaredMethod("postProcessBeanFactory");
                CtMethod mold = CtNewMethod.copy(ctMethod, "postProcessBeanFactoryOld", ctClass, null);
                ctClass.addMethod(mold);
                ctMethod.setBody(code);

                if (ctClass.isFrozen()) {
                    ctClass.defrost();
                }
                ctClass.toClass();
                LogUtils.info(ApolloExtend.class, ApolloProperties.Project, "注入Apollo PropertySourcesProcessor ok");
            }
        } catch (Exception exp) {
            LogUtils.error(ApolloExtend.class,  ApolloProperties.Project, "注入Apollo PropertySourcesProcessor 异常", exp);
        }
    }

    public static void refreshApolloPropertySources(){
        if(!PropertyUtils.getPropertyCache(ApolloProperties.BsfApolloLocalFirstEnabled,true))
            return;
        var environment=ContextUtils.getApplicationContext().getEnvironment();
        if (environment.getPropertySources().contains("ApolloBootstrapPropertySources")) {
            setLast(environment,"ApolloBootstrapPropertySources");
            setLast(environment,"ApolloPropertySources");
        } else {
            setLast(environment,"ApolloPropertySources");
        }
    }

    protected static void setLast(ConfigurableEnvironment environment, String source){
        val propertySources=environment.getPropertySources();
        PropertySource<?> bootstrapPropertySource = propertySources.get(source);
        if (bootstrapPropertySource != null&&propertySources.precedenceOf(bootstrapPropertySource)!=(propertySources.size()-1)) {
            propertySources.remove(source);
            propertySources.addLast(bootstrapPropertySource);
        }
    }

}
